﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;


namespace Netfraction
{
    public class ChatDocument
    {
        public static Paragraph CreateChatDocument(Netfraction.Identity user, string text)
        {
            Paragraph para = new Paragraph();

            try
            {
                bool slashMsg = text.StartsWith("/me ", StringComparison.InvariantCulture);
                text = slashMsg ? text.Substring(4) : text;

                if (text == "/join" || text == "/leave")
                {
                    para.Inlines.Add(CreateChatRun(user, text == "/join" ? "has joined the channel" : "has left the channel"));
                    para.Inlines.InsertBefore(para.Inlines.FirstInline, CreateUserNoticeString(user));
                    return para;
                }

                para.Inlines.Add(CreateChatRun(user, text));

                ReplaceURL(ref para);
                ReplaceMagnetUri(ref para);
                ReplaceMyNick(ref para);
                ReplaceOperatorNick(ref para);

                CreateEmoticons(ref para);

                para.Inlines.InsertBefore(para.Inlines.FirstInline, CreateUserString(user, slashMsg));
                //para.Inlines.InsertAfter(para.Inlines.FirstInline, CreateEmo());
                if (ClientSettings.Appearance.TimeStamp.Enabled)
                {
                    para.Inlines.InsertBefore(para.Inlines.FirstInline, CreateTimeStamp());
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.StackTrace);
            }
            return SetChatURLHandler(para);
        }

        public static Run CreateChatRun(Netfraction.Identity user, string text)
        {
            try
            {
                Color runColour = (MainWindow.localIdentity.Nickname == user.Nickname) ? ClientSettings.Appearance.MyText.Colour : ClientSettings.Appearance.MessageText.Colour;
                Run chatRun = new Run(text);
                chatRun.Foreground = new SolidColorBrush(runColour);
                return chatRun;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            return null;
        }

        public static void ReplaceMyNick(ref Paragraph para)
        {
            try
            {
                TextRange tr;
                string xamlString = TextRangeToXamlString(para, out tr);

                System.Text.RegularExpressions.MatchEvaluator CatchRun = delegate(System.Text.RegularExpressions.Match m)
                {
                    string prepend = "";
                    string append = "";
                    if (m.Groups["prepend"].Length != 0)
                    {
                        prepend = m.Groups["prepend"].Value + "</Run>";
                    }
                    if (m.Groups["append"].Length != 0)
                    {
                        append = "<Run>" + m.Groups["append"].Value;
                    }
                    
                    return String.Format("{0}<Run Foreground=\"" + ClientSettings.Appearance.MyNick.Colour.ToString() + "\">{1}</Run>{2}", prepend, MainWindow.localIdentity.Nickname, append);
                };

                int replacementStartPos = xamlString.IndexOf("<Run>");
                string regexMatch = @"(?<prepend>.*)" + System.Text.RegularExpressions.Regex.Escape(MainWindow.localIdentity.Nickname) + "(?<append>.*)";
                System.Text.RegularExpressions.Regex findLocalNick = new System.Text.RegularExpressions.Regex(regexMatch, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                xamlString = findLocalNick.Replace(xamlString, CatchRun, 1000, replacementStartPos);
                xamlString = xamlString.Replace("<Run></Run>", "");
                XamlStringToTextRange(ref tr, xamlString);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        public static void ReplaceOperatorNick(ref Paragraph para)
        {
            try
            {
                TextRange tr;
                string xamlString = TextRangeToXamlString(para, out tr);

                System.Text.RegularExpressions.MatchEvaluator CatchRun = delegate(System.Text.RegularExpressions.Match m)
                {
                    string prepend = "";
                    string append = "";
                    if (m.Groups["prepend"].Length != 0)
                    {
                        prepend = m.Groups["prepend"].Value + "</Run>";
                    }
                    if (m.Groups["append"].Length != 0)
                    {
                        append = "<Run>" + m.Groups["append"].Value;
                    }

                    return String.Format("{0}<Run Foreground=\"" + ClientSettings.Appearance.OperatorNick.Colour.ToString() + "\">{1}</Run>{2}", prepend, m.Groups["nick"], append);
                };

                int replacementStartPos = xamlString.IndexOf("<Run>");
                foreach (Netfraction.Identity identity in MainWindow.mClient.Peers.Select(c => c.Identity))
                {
                    if (identity.IsOperator)
                    {
                        string regexMatch = @"(?<prepend>.*)(?<nick>(" + System.Text.RegularExpressions.Regex.Escape(identity.Nickname) + "))(?<append>.*)";
                        System.Text.RegularExpressions.Regex findOperatorNick = new System.Text.RegularExpressions.Regex(regexMatch, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                        xamlString = findOperatorNick.Replace(xamlString, CatchRun, 1000, replacementStartPos);
                        xamlString = xamlString.Replace("<Run></Run>", "");
                        /*string regex = @"(?<!<Hyperlink.*>)(?:<Run>)(?<text>.*" + System.Text.RegularExpressions.Regex.Escape(identity.Nickname) + ".*)(?:</Run>)(?!</Hyperlink>)";
                        System.Text.RegularExpressions.Regex findOperatorNick = new System.Text.RegularExpressions.Regex(regex, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                        xamlString = findOperatorNick.Replace(xamlString, "</Run><Run Foreground=\"Purple\">" + identity.Nickname + "</Run><Run>", 1000, replacementStartPos);*/
                    }
                }
                XamlStringToTextRange(ref tr, xamlString);

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        public static void ReplaceURL(ref Paragraph para)
        {
            try
            {
                TextRange tr;
                string xamlString = TextRangeToXamlString(para, out tr);

                /*System.Text.RegularExpressions.MatchEvaluator CatchRun = delegate(System.Text.RegularExpressions.Match m)
                {
                    string retVal = "";
                    string prepend = "";
                    string append = "";
                    if (m.Groups["prepend"].Length != 0)
                    {
                        prepend = "<Run>" + m.Groups["prepend"].Value + "</Run>";
                    }
                    if (m.Groups["append"].Length != 0)
                    {
                        append = "<Run>" + m.Groups["append"].Value + "</Run>";
                    }
                    retVal = String.Format("{0}<Hyperlink NavigateUri=\"{1}\">{1}</Hyperlink>{2}", prepend, m.Groups["url"], append);
                    return retVal;
                };*/

                System.Text.RegularExpressions.MatchEvaluator CatchRun = delegate(System.Text.RegularExpressions.Match m)
                {
                    string retVal = "";
                    string prepend = "";
                    string append = "";
                    if (m.Groups["prepend"].Length != 0)
                    {
                        prepend = "<Run>" + m.Groups["prepend"].Value + "</Run>";
                    }
                    if (m.Groups["append"].Length != 0)
                    {
                        append = "<Run>" + m.Groups["append"].Value + "</Run>";
                    }
                    if (System.Uri.IsWellFormedUriString(m.Groups["url"].Value, UriKind.Absolute))
                    {
                        retVal = String.Format("{0}<Hyperlink NavigateUri=\"{1}\">{1}</Hyperlink>{2}", "</Run>", m.Groups["url"], "<Run>");
                    }// RequestNavigate=\"chatDoc_LinkClicked\"
                    else
                    {
                        retVal = m.Groups["url"].Value;
                    }
                    return retVal;
                };

                int replacementStartPos = xamlString.IndexOf("<Run>");
                //string regexMatch = @"(?:<Run>)(?<prepend>.*)(?<url>http://[^ ]+)(?<append>.*)(?:</Run>)"
                //string regexMatch = @"(?<prepend>.*)(?<url>(https|http|adc)://[^ \n]+)(?<append>.*)";
                string regexMatch = @"(?<url>(?<protocol>https|http|adc)://[-A-Za-z0-9+&@#/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#/%=~_()|])";
                System.Text.RegularExpressions.Regex findUrl = new System.Text.RegularExpressions.Regex(regexMatch, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                //xamlString = findUrl.Replace(xamlString, "</Run>${prepend}<Hyperlink NavigateUri=\"${url}\">${url}</Hyperlink>${append}<Run>", 1000, replacementStartPos);
                xamlString = findUrl.Replace(xamlString, CatchRun, 1000, replacementStartPos);
                xamlString = xamlString.Replace("<Run></Run>", "");
                XamlStringToTextRange(ref tr, xamlString);

                //para = SetChatURLHandler(para);

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        public static Paragraph SetChatURLHandler(Paragraph para)
        {
            Hyperlink link;

            // Find programatically added Hyperlink and add a Click-EventHandler.
            foreach (ContentElement member in para.Inlines)
            {
                link = member as Hyperlink;
                if (link != null)
                {
                    link.Foreground = new SolidColorBrush(ClientSettings.Appearance.UrlText.Colour);
                    link.Click += new RoutedEventHandler(chatDoc_LinkClicked);
                    break;
                }
            }
            return para;
        }

        public static void ReplaceMagnetUri(ref Paragraph para)
        {
            try
            {
                TextRange tr;
                string xamlString = TextRangeToXamlString(para, out tr);

                System.Text.RegularExpressions.MatchEvaluator CatchRun = delegate(System.Text.RegularExpressions.Match m)
                {
                    string retVal = "";
                    string prepend = "";
                    string append = "";
                    if (m.Groups["prepend"].Length != 0)
                    {
                        prepend = "<Run>" + m.Groups["prepend"].Value + "</Run>";
                    }
                    if (m.Groups["append"].Length != 0)
                    {
                        append = "<Run>" + m.Groups["append"].Value + "</Run>";
                    }
                    if (System.Uri.IsWellFormedUriString(m.Groups["url"].Value, UriKind.Absolute))
                    {
                        string[] magnetParams = m.Groups["params"].Value.Split(new string[] { "&amp;" }, StringSplitOptions.RemoveEmptyEntries);
                        string friendlyName = m.Groups["url"].Value;
                        string urnValue = string.Empty;
                        string fileSize = string.Empty;
                        foreach (string s in magnetParams)
                        {
                            if (s.StartsWith("xt="))
                            {
                                urnValue = s.Substring(3 + 15);
                            }
                            if (s.StartsWith("dn="))
                            {
                                friendlyName = s.Substring(3).Replace("+", " ");
                            }
                            if (s.StartsWith("xl="))
                            {
                                double fSize = 0f;
                                if (double.TryParse(s.Substring(3), out fSize))
                                {
                                    fileSize = string.Format(" ({0} MB)", Math.Round(fSize / Math.Pow(2, 20), 2));
                                }
                            }
                        }
                        retVal = String.Format("{0}<Hyperlink NavigateUri=\"{1}\">{2}{3}</Hyperlink>{4}", "</Run>", urnValue, friendlyName, fileSize, "<Run>");
                    }
                    else
                    {
                        retVal = m.Groups["url"].Value;
                    }
                    return retVal;
                };

                int replacementStartPos = xamlString.IndexOf("<Run>");
                //string regexMatch = @"(?:<Run>)(?<prepend>.*)(?<url>http://[^ ]+)(?<append>.*)(?:</Run>)"
                //string regexMatch = @"(?<prepend>.*)(?<url>(https|http|adc)://[^ \n]+)(?<append>.*)";
                string regexMatch = @"(?<url>(?<protocol>magnet):\?(?<params>[-A-Za-z0-9+&@#/%?=~_()|!:,.;']*[-A-Za-z0-9+&@#/%=~_()|']))";
                System.Text.RegularExpressions.Regex findUrl = new System.Text.RegularExpressions.Regex(regexMatch, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                //xamlString = findUrl.Replace(xamlString, "</Run>${prepend}<Hyperlink NavigateUri=\"${url}\">${url}</Hyperlink>${append}<Run>", 1000, replacementStartPos);
                xamlString = findUrl.Replace(xamlString, CatchRun, 1000, replacementStartPos);
                xamlString = xamlString.Replace("<Run></Run>", "");
                XamlStringToTextRange(ref tr, xamlString);

                //para = SetChatURLHandler(para);

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        public static Run CreateUserString(Netfraction.Identity user)
        {
            return CreateUserString(user, false);
        }

        public static Run CreateUserString(Netfraction.Identity user, bool slashMeMsg)
        {
            Color userColour = MainWindow.localIdentity.Nickname == user.Nickname ? ClientSettings.Appearance.MyNick.Colour : (user.IsOperator ? ClientSettings.Appearance.OperatorNick.Colour : ClientSettings.Appearance.Nick.Colour);

            Run userRun = null;
            if (slashMeMsg)
            {
                userRun = new Run(String.Format("* {0} ", user.Nickname));
            }
            else
            {
                switch (ClientSettings.Appearance.ChatStyle)
                {
                    case ClientSettings.Appearance.ChatStyleEnum.Inline:
                        userRun = new Run(String.Format("<{0}> ", user.Nickname));
                        break;
                    case ClientSettings.Appearance.ChatStyleEnum.NewLine:
                        userRun = new Run(String.Format("{0} says:\n", user.Nickname));
                        break;
                    default:
                        userRun = new Run();
                        break;
                }
            }

            userRun.Foreground = new SolidColorBrush(userColour);
            userRun.FontWeight = FontWeights.Bold;
            return userRun;
        }

        public static Run CreateUserNoticeString(Netfraction.Identity user)
        {
            Color userColour = MainWindow.localIdentity.Nickname == user.Nickname ? ClientSettings.Appearance.MyNick.Colour : (user.IsOperator ? ClientSettings.Appearance.OperatorNick.Colour : ClientSettings.Appearance.Nick.Colour);

            Run userRun = null;
            userRun = new Run(String.Format("--{0}-- ", user.Nickname));

            userRun.Foreground = new SolidColorBrush(userColour);
            userRun.FontWeight = FontWeights.Bold;
            return userRun;
        }

        public static Run CreateTimeStamp()
        {
            Run timestamp = new Run(String.Format("[{0}] ", DateTime.Now.ToString(ClientSettings.Appearance.TimeStamp.Format)));
            timestamp.Foreground = new SolidColorBrush(ClientSettings.Appearance.TimeStamp.Colour);

            return timestamp;
        }

        public static void CreateEmoticons(ref Paragraph para)
        {
            try
            {
                // need to store the actual emoticon string sequence somewhere so it can be displayed in the UI
                // regular expression, image address
                //emoticonList.Add(@":\)", "pack://application:,,,/Images/Emoticons/Image001.png");

                string curKey = string.Empty;
                System.Text.RegularExpressions.MatchEvaluator CatchRun = delegate(System.Text.RegularExpressions.Match m)
                {
                    string retVal = string.Empty;
                    string prepend = string.Empty;
                    string append = string.Empty;

                    if (m.Groups["prepend"].Length != 0)
                        prepend = "<Run>" + m.Groups["prepend"].Value + "</Run>";

                    if (m.Groups["append"].Length != 0)
                        append = "<Run>" + m.Groups["append"].Value + "</Run>";

                    if (m.Groups["emoticon"].Length != 0)
                        retVal = "<Image Source=\"" + System.IO.Path.Combine(MainWindow.EmoticonsDir, MainWindow.emoticonList[curKey]) + "\" Height=\"19\" Width=\"19\" Stretch=\"Uniform\" /><Run></Run>";

                    if (string.IsNullOrEmpty(prepend) && string.IsNullOrEmpty(append))
                        append = "<Run />"; // for some reason it wont work if there is no other text

                    return prepend + retVal + append;
                };
                
                TextRange tr = null;
                string xamlString;
                xamlString = TextRangeToXamlString(para, out tr);

                int replacementStartPos = xamlString.IndexOf("<Run>");
                foreach (KeyValuePair<string, string> emoticon in MainWindow.emoticonList)
                {
                    curKey = emoticon.Key;
                    System.Text.RegularExpressions.Regex findEmoticon = new System.Text.RegularExpressions.Regex("<Run>(?<prepend>.*)(?<emoticon>" + emoticon.Key + ")(?<append>.*)</Run>");
                    xamlString = findEmoticon.Replace(xamlString, CatchRun, 1000, replacementStartPos);
                }

                XamlStringToTextRange(ref tr, xamlString);
            }
            catch (System.IO.IOException ioex)
            {
                Console.WriteLine(ioex.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
        
        private static void XamlStringToTextRange(ref TextRange tr, string xamlString)
        {
            System.IO.MemoryStream msOut = new System.IO.MemoryStream();
            System.IO.StreamWriter sw = new System.IO.StreamWriter(msOut);
            sw.Write(xamlString);
            sw.Flush();
            msOut.Seek(0, System.IO.SeekOrigin.Begin);
            tr.Load(msOut, DataFormats.Xaml);
        }

        private static string TextRangeToXamlString(Paragraph para, out TextRange tr)
        {
            tr = new TextRange(para.ContentStart, para.ContentEnd);
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            tr.Save(ms, DataFormats.Xaml);
            //return ASCIIEncoding.Default.GetString(ms.ToArray());
            return Encoding.UTF8.GetString(ms.ToArray());
        }

        public static void chatDoc_LinkClicked(object sender, RoutedEventArgs e)
        {
            Hyperlink senderLink = sender as Hyperlink;
            if (senderLink != null)
            {
                try
                {
                    if (senderLink.NavigateUri.IsWellFormedOriginalString())
                    {
                        System.Diagnostics.Process.Start(senderLink.NavigateUri.ToString());
                    }                    
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
    }
}
